A memory on any computing system is a limited resource. No matter how fast computing systems become, they always depend upon a finite amount of memory in which to run their software applications. As a result, software developers should consider this resource when writing and developing software applications.
The Java programming language differs from many traditional programming languages (e.g., C, C++, C#) by the way in which memory is allocated and deallocated. In languages like C, C++ and C#, memory is explicitly allocated and deallocated by the application programmer/developer. This can greatly increase the time spent by programmers in tracking down coding defects in regards to deallocating memory.
By contrast, the Java runtime environment (e.g., Java virtual machine) provides a built-in mechanism for allocating and deallocating memory. In Java, memory is allocated to objects. The Java virtual machine (“VM” or “JVM”) automatically handles the amount and allocation of memory upon an object's creation. The Java runtime environment employs a “garbage collector” (GC) to reclaim the memory allocated to an object that is no longer needed. Once the GC determines that the object is no longer accessible (e.g., when there is no longer any references to it stored in any variables, the fields of objects, or the elements of any arrays, etc.), it reclaims the allocated memory. When objects in a Java application are no longer referenced, the heap space the object occupied is to be recycled so that the space becomes available for subsequently-created objects.
Although having garbage collection improves productivity, it is not entirely immune from a class of bugs, called “memory leaks.” A memory leak can occur when a program (or in the case of Java, the VM) allocates memory to an object but never (or only partially) deallocates the memory when the object is no longer needed. As a result, a continually increasing block of memory may be allocated to the object, eventually resulting in an “Out Of Memory Error” (OOME). In other words, a memory leak occurs when memory is allocated, but it is never (or only partially) reclaimed. Memory leaks can also occur when a data structure (e.g., hashtable) is used to associated one object with another and even when neither object is required any longer, the association with the data structure remains, preventing the objects from being reclaims until the data structure is reclaimed. Stated differently, when a lifetime of the data structure is longer than that of the objects associated with it, memory leaks are caused.
Memory leaks are of particular concern on Java-based systems (e.g., Java 2 Platform Enterprise Edition (J2EE) platforms) which are to run twenty-four hours a day, seven days a week. In this case, memory leaks, even seemingly insignificant ones, can become a major problem. Even the smallest memory leak in code that runs 24/7 may eventually cause an OOME, which can bring down the VM and its applications.
Knowing how to track down memory leaks is essential to having a solid program design. There are many performance and/or debugging tools that are used to monitor and examine software applications to determine resource consumption within the Java runtime environment. For example, a profiling tool may identify the most frequently executed methods and objects created in an application. Another type of software performance and debugging tool is a “tracer.” However, such tools are very limited in detection of memory leaks, while consuming great amounts of system resources by requiring starting and restarting of VMs in special modes. Further, such tools are also limited in providing information on how the memory leaks occur. This leaves developers with often insurmountable amounts of code to manually evaluate to find the specific class and method calls, etc. Moreover, although these tools may provide certain statistics on the memory allocation for objects within a running application, such information may not be useful for an application that comprises several thousand objects.
Conventional profiling tools (e.g., Optimizelt and JProbe), when used, require restarting of VMs and the server, which results in loss of production and system resources, particularly when restarting a productive system. Moreover, the starting of the server and its VMs further adds to the system overhead by increasing memory consumption, which also harms the normal work of the server and server software. The restarting of the server adds overhead in regards to the Central Processing Unit (CPU), as the server would have to start up from scratch. The memory consumption should be the same (when the server is shut down, the memory is freed, when it starts up, it's allocated again). These profiling tools require additional memory to operate. Furthermore, these tools require manual interaction with regard to the functioning of the GC, which is not only tedious in nature, but also results in loss of previously-collected profiling information each time an OOME occurs.